package com.rupertjones.globalcron.domain.scheduling;

import com.rupertjones.globalcron.domain.JobDescriptor;
import org.apache.log4j.Logger;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import static java.lang.String.format;

public class Executable implements Runnable {

    private JobDescriptor descriptor;
    private static final Logger LOG = Logger.getLogger(Executable.class);
    private StringBuilder output = new StringBuilder();
    private boolean executedNormally = true;

    public Executable(JobDescriptor descriptor) {
        this.descriptor = descriptor;
    }

    @Override
    public void run() {
        Runtime run = Runtime.getRuntime();
        Process process = null;

        try {
            process = run.exec(descriptor.getScript());
            waitForCompletionIfRequired(process);
            captureOutput(process);
            waitIfRequired();
            checkExitValue(process);
        } catch (Throwable e) {
            executedNormally = false;
            output.append(e.getMessage());
            LOG.error(format("Problem executing: %s", descriptor.getScript()), e);
        } finally {
             try {
                if (process != null) {
                    process.getInputStream().close();
                    process.getErrorStream().close();
                }
            } catch (Throwable th) {
                LOG.error(format("Problem closing stream from process: %s", descriptor.getScript()), th);
            }
        }
    }

    private void checkExitValue(Process process) {
        if (process.exitValue() != 0) {
            executedNormally = false;
            LOG.warn(format("Exit value for [%s] was non zero, possible problem", descriptor.getScript()));
        }
    }

    private void captureOutput(Process process) throws IOException {
        final BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line = br.readLine()) != null) {
            output.append(format("%s\n", line));
        }
        br.close();
    }

    private void waitForCompletionIfRequired(Process process) throws InterruptedException {
        if (descriptor.isWaitForCompletion()) {
            process.waitFor();
        }
    }

    private void waitIfRequired() throws InterruptedException {
        if (descriptor.hasWaitAfter()) {
            if (LOG.isDebugEnabled()) {
                LOG.debug(format("Waiting after execution completes [%s ms]", descriptor.getWaitAfter()));
            }
            Thread.sleep(descriptor.getWaitAfter());
        }
    }

    public boolean executedNormally() {
        return executedNormally;
    }

    public String getOutput() {
        return output.toString();
    }
}
